package com.bridgeintelligent.tag.bulkload.service.core;

import com.bridgeintelligent.tag.bulkload.api.*;
import com.bridgeintelligent.tag.bulkload.api.fileload.FileLoadModel;
import com.bridgeintelligent.tag.bulkload.api.fileload.FilterGroup;
import com.bridgeintelligent.tag.bulkload.api.issue.CustomersChannel;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueLog;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueResult;
import com.bridgeintelligent.tag.bulkload.api.issue.IssueTaskLog;
import com.bridgeintelligent.tag.bulkload.service.core.cache.CacheConfig;
import com.bridgeintelligent.tag.bulkload.service.core.cache.CacheUtils;
import com.bridgeintelligent.tag.bulkload.service.core.issue.IssueIftService;
import com.bridgeintelligent.tag.bulkload.service.es.ESUtils;
import com.bridgeintelligent.tag.bulkload.service.es.query.EsQueryBuilder;
import com.bridgeintelligent.tag.mapper.DimDataMapper;
import com.bridgeintelligent.tag.mapper.EsTimeMapper;
import com.bridgeintelligent.tag.mapper.IssueTaskMapper;
import com.bridgeintelligent.tag.utils.DateHelper;
import com.bridgeintelligent.tag.utils.FieldHandler;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @description
 * @author: grp 2021-03-04 22:01
 **/
@Slf4j
@AllArgsConstructor
@Service
public class IssueHandler {
    private static final String SPLIT_SIGN = ",";

    private static final String DIVIDE_SIGN = "|";

    private static final String LOCAL_FILE_FORMAT = "/data/issue/%s_%s_%s";

    private static final String LOCAL_FILE_FORMAT_DATA = "%s_data.txt";

    private static final String LOCAL_FILE_FORMAT_DICT = "%s_dict.txt";

    private static final String LOCAL_FILE_FORMAT_CHECK = "%s_check.txt";

    private static final String SUB_SIGN = "_";

    private static final String LAST_CLOSING_DATE = "last_closing_date";

    private final IssueTaskMapper issueTaskMapper;

    private IssueIftService issueIftService;

    private DimDataMapper dimDataMapper;

    private EsTimeMapper esTimeMapper;

    public IssueResult handler(IssueTaskLog issueTaskLog, IssueLog issueLog, FilterGroup filterGroup) throws ParseException {


        FileLoadModel fileLoadModel = createFileLoad(issueTaskLog,issueLog.getDataDate(), filterGroup);

        CustomersChannel customersChannel = issueTaskMapper.findChannel(issueTaskLog.getChannelId());

        String dataDate = issueIftService.getDate(issueLog.getDataDate());

        String tempFilePre = issueIftService.getTempPath(issueTaskLog,dataDate, customersChannel,LOCAL_FILE_FORMAT);

        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_DATA,tempFilePre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile()
                    .mkdir();
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fos))) {
            Map<String, String> realKeymap = new LinkedHashMap<>(fileLoadModel.getIncludes().length);
            String lastClose = "";
            for (String s : fileLoadModel.getIncludes()) {
                if (s.startsWith(LAST_CLOSING_DATE)){
                    lastClose = s;
                }else{
                    realKeymap.put(s, FieldHandler.reduceSuffixField(s,issueLog.getDataDate()));
                }
            }
            IndexNameInfo indexNameInfo = dimDataMapper.findIndexInfoById(issueTaskLog.getIndexId());

            if(indexNameInfo==null){
                Thread.sleep(60000);
            }
            AtomicInteger num = new AtomicInteger();
            if(indexNameInfo==null){
                throw new BulkLoadException(new ExceptionCode(999, "索引信息为空"));
            }
            writeHandler(indexNameInfo, fileLoadModel, num, bufferedWriter,lastClose);
            File okFile = createOkFile(fileLoadModel,num,realKeymap,tempFilePre);
            File dictFile = createDictFile(fileLoadModel,realKeymap,tempFilePre);
            bufferedWriter.flush();
            checkFile(dataTempFile,num);
            return new IssueResult().setNum(num.get()).setFilePath(issueIftService.upload(dataDate,
                    issueTaskLog,issueLog,customersChannel,dataTempFile,dictFile,okFile));
        } catch (IOException e) {
            log.error("写入本地文件出现异常", e);
            throw new BulkLoadException(new ExceptionCode(999, "写入本地文件出现异常"));
        }catch (NullPointerException e) {
            log.error("查询结果为空", e);
            throw new BulkLoadException(new ExceptionCode(999, "查询结果为空"));
        } catch (InterruptedException e) {
            log.error("线程休眠异常",e);
            throw new BulkLoadException(new ExceptionCode(999, "生成下发文件出现中断异常"));
        }
    }

    private File createDictFile(FileLoadModel fileLoadModel, Map<String, String> realKeymap,String pre) throws IOException {
        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_DICT,pre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile()
                    .mkdir();
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter =
                new BufferedWriter(new OutputStreamWriter(fos))) {
            for (String s : fileLoadModel.getIncludes()) {
                if (!s.startsWith(LAST_CLOSING_DATE)){
                    String realKey = realKeymap.get(s);
                    List<DimItemInfo> list = dimDataMapper.findItemList(realKey);
                    for(DimItemInfo dimItemInfo:list){
                        if(dimItemInfo==null){
                            continue;
                        }
                        StringBuilder stringBuilder = new StringBuilder();
                        stringBuilder.append(realKey)
                                .append(DIVIDE_SIGN).append(dimItemInfo.getDimItemNo())
                                .append(DIVIDE_SIGN).append(dimItemInfo.getDimItemName());
                        bufferedWriter.write(stringBuilder.toString());
                        bufferedWriter.newLine();
                    }
                }
            }
            bufferedWriter.flush();
            return dataTempFile;
        }
    }

    private File createOkFile(FileLoadModel fileLoadModel, AtomicInteger num, Map<String, String> realKeymap,String pre) throws IOException {
        File dataTempFile = new File(String.format(LOCAL_FILE_FORMAT_CHECK,pre));
        if (!dataTempFile.exists()) {
            dataTempFile.getParentFile()
                    .mkdir();
        }
        StringBuilder ids = new StringBuilder();
        StringBuilder names = new StringBuilder();
        for (String s : fileLoadModel.getIncludes()) {
            if (!s.startsWith(LAST_CLOSING_DATE)){
                ids.append(realKeymap.get(s))
                        .append(DIVIDE_SIGN);
                String dimName = Optional.ofNullable((DimInfo) CacheUtils.getCacheInfo(CacheConfig.DIM_INFO, realKeymap.get(s)))
                        .map(DimInfo::getDimName)
                        .orElse(SUB_SIGN);
                names.append(dimName)
                        .append(DIVIDE_SIGN);
            }
        }
        try (FileOutputStream fos = new FileOutputStream(dataTempFile); BufferedWriter bufferedWriter =
                new BufferedWriter(new OutputStreamWriter(fos))) {
            bufferedWriter.write(ids.toString());
            bufferedWriter.newLine();
            bufferedWriter.write(names.toString());
            bufferedWriter.newLine();
            bufferedWriter.write(String.valueOf(num));
            bufferedWriter.newLine();
            bufferedWriter.flush();
            return dataTempFile;
        }catch (Exception e){
            throw e;
        }
    }

    private void checkFile(File file, AtomicInteger num) throws IOException {
        long count = Files.lines(Paths.get(file.getPath())).count();
        if(count == num.intValue()){
            return;
        }
        log.info("下发文件校验{}-{}",num,count);
        throw new BulkLoadException(new ExceptionCode(999, "生成的文件校验失败"));
    }

    private void writeHandler(IndexNameInfo indexNameInfo, FileLoadModel fileLoadModel, AtomicInteger num, BufferedWriter bufferedWriter,String lastClose) {
        ESUtils.scrollSearch(indexNameInfo.getDataDate(), fileLoadModel, hits -> {
            write(fileLoadModel, num, bufferedWriter, hits,lastClose);
        });
    }

    private void write(FileLoadModel fileLoadModel, AtomicInteger num, BufferedWriter bufferedWriter, SearchHit[] hits,String lastClose) throws IOException {
        int n=0;
        for (SearchHit hit : hits) {
            StringBuilder tmp = new StringBuilder();
            num.getAndIncrement();

            //特殊处理逻辑
            Object checkClose =  hit.getSourceAsMap().get(lastClose);
            if (checkClose != null && Integer.parseInt(checkClose.toString().substring(0,4)) <= Integer.parseInt(DateHelper.currentDateTime(DateHelper.PATTERN_DATE_6))){
                for (String feild : fileLoadModel.getIncludes()) {
                    if (!feild.startsWith(LAST_CLOSING_DATE)){
                        Object source = hit.getSourceAsMap().get(feild);
                        if (feild.equals(BulkLoadConfig.CUST_NO) || feild.equals(BulkLoadConfig.ECIF_ID)){
                            String queryResult = source == null ? SUB_SIGN : source.toString();
                            tmp.append(queryResult).append(DIVIDE_SIGN);
                        }else{
                            tmp.append(SUB_SIGN).append(DIVIDE_SIGN);
                        }
                    }

                }
            }else{
                for(String feild:fileLoadModel.getIncludes()){
                    if (!feild.startsWith(LAST_CLOSING_DATE)){
                        Object source = hit.getSourceAsMap()
                                .get(feild);
                        if(source == null){
                            tmp.append("")
                                    .append(DIVIDE_SIGN);
                        }else if(source instanceof List){
                            List<Object> values = (List<Object>) source;
                            String result =
                                    values.stream()
                                            .distinct()
                                            .filter(value-> StringUtils.isNotBlank(String.valueOf(value)))
                                            .map(Object::toString)
                                            .collect(Collectors.joining(","));
                            tmp.append(result)
                                    .append(DIVIDE_SIGN);
                        } else {
                            String queryResult = source.toString();
                            tmp.append(queryResult)
                                    .append(DIVIDE_SIGN);
                        }
                    }
                }
            }
            bufferedWriter.write(tmp.toString());
            bufferedWriter.newLine();
        }
    }

    private FileLoadModel createFileLoad(IssueTaskLog issueTaskLog,String dataDate, FilterGroup filterGroup) {
        FileLoadModel fileLoadModel = new FileLoadModel();
        if (FieldHandler.FALSE.equals(issueTaskLog.getTimeType())){
            fileLoadModel.setQueryBuilder(EsQueryBuilder.buildFilter(filterGroup));
        }else{
            fileLoadModel.setQueryBuilder(QueryBuilders.termsQuery(FieldHandler.CUSTOMER_FLAG,issueTaskLog.getCustomersId()));
        }
        fileLoadModel.setExcludes(Strings.EMPTY_ARRAY);
        fileLoadModel.setIncludes(choseIncludes(issueTaskLog.getFields(),dataDate,issueTaskLog.getIndexId()));
        return fileLoadModel;
    }

    private String[] choseIncludes(String fields, String dataDate,String indexId) {

        return Arrays.stream(fields.split(SPLIT_SIGN)).filter(StringUtils::isNotEmpty)
                .map(s -> {
                    if (s.equals(FieldHandler.CUST_NO) || s.equals(FieldHandler.ECIF_ID)){
                        return s;
                    }else{
                        if (FieldHandler.pattern.matcher(dataDate).matches() && dataDate.length() == FieldHandler.LENGTH8 ){
                            return s;
                        }else{
                            //根据索引查询上月末的后缀标识
                            String esSymbol = "";
                            if (FieldHandler.INDEX_ONE.equals(indexId) ){
                                esSymbol = Optional.ofNullable(esTimeMapper.findEsSymbol(indexId,FieldHandler.LAST_MONTH_ONE)).orElse(FieldHandler.LAST_MONTH_ONE);
                            }else{
                                esSymbol = Optional.ofNullable(esTimeMapper.findEsSymbol(indexId,FieldHandler.LAST_MONTH_TWO)).orElse(FieldHandler.LAST_MONTH_TWO);
                            }
                            return String.format(FieldHandler.ID_FORMAT, s, esSymbol);
                        }
                    }
                })
                .toArray(String[]::new);
    }
}
